//*************************************************************************************
// EGTB Generator Lowest Level Access Functions for Raw Uncompressed database files
//*************************************************************************************

#pragma once
#include "DatabaseRaw.h"

// Templated functions for the raw uncompressed database file access
//////////////////////////////////////////////////////////////////////////////////

namespace EndGameDB
{
	// Returns the subdatabase slice size in the number of items - WDL positions
	///////////////////////////////////////////////////////////////////////////////////////
	template<int BitsPerRow, int MaxField>
	int64 inline GetDatabaseSize(int wm, int wk, int bm, int bk, int wmrank, int bmrank)
	{
		int64 dbsize = 1;

		if(bm)
			dbsize *= BinomCoef[BitsPerRow*(bmrank+1)][bm] - BinomCoef[BitsPerRow*bmrank][bm];
  
		if(wm)
			dbsize *= BinomCoef[BitsPerRow*(wmrank+1)][wm] - BinomCoef[BitsPerRow*wmrank][wm];

		// number of bk configurations
		if(wk)
			dbsize *= BinomCoef[MaxField+1-bm-wm][wk];

		// number of wk configurations
		if(bk)
			dbsize *= BinomCoef[MaxField+1-wm-bm-wk][bk];

		return dbsize;
	}


	// Initializes internal arrays and variables for database raw access layer
	// of EGTB Generator
	///////////////////////////////////////////////////////////////////////////////////////
	template<int BitsPerRow, int MaxField>
	void InitDatabaseRaw()
	{
		omp_init_lock(&lockMemoryPanic);

		memInUse = 0;
		memoryLimit = 0;
		currentDb_b = NULL;
		currentDb_w = NULL;

		for(int i=0;i<=MAXPIECE;i++)
		{
			for(int j=0;j<=MAXPIECE;j++)
			{
				for(int k=0;k<=MAXPIECE;k++)
				{
					for(int l=0;l<=MAXPIECE;l++)
					{
						for(int m=0;m<9;m++)
						{
							for(int n=0;n<9;n++)
							{
								SubDatabase[i][j][k][l][m][n][0].bm = i;
								SubDatabase[i][j][k][l][m][n][0].bk = j;
								SubDatabase[i][j][k][l][m][n][0].wm = k;
								SubDatabase[i][j][k][l][m][n][0].wk = l;
								SubDatabase[i][j][k][l][m][n][0].maxwm = m;
								SubDatabase[i][j][k][l][m][n][0].maxbm = n;
								SubDatabase[i][j][k][l][m][n][0].database = NULL;
								SubDatabase[i][j][k][l][m][n][0].db_dtm = NULL;
								SubDatabase[i][j][k][l][m][n][0].databasesize = GetDatabaseSize<BitsPerRow, MaxField>(i, j, k, l, m, n);
								SubDatabase[i][j][k][l][m][n][0].db_dtm_size = GetDatabaseSize<BitsPerRow, MaxField>(i, j, k, l, m, n);
								SubDatabase[i][j][k][l][m][n][1].bm = i;
								SubDatabase[i][j][k][l][m][n][1].bk = j;
								SubDatabase[i][j][k][l][m][n][1].wm = k;
								SubDatabase[i][j][k][l][m][n][1].wk = l;
								SubDatabase[i][j][k][l][m][n][1].maxwm = m;
								SubDatabase[i][j][k][l][m][n][1].maxbm = n;
								SubDatabase[i][j][k][l][m][n][1].databasesize = GetDatabaseSize<BitsPerRow, MaxField>(i, j, k, l, m, n);
								SubDatabase[i][j][k][l][m][n][1].db_dtm_size = GetDatabaseSize<BitsPerRow, MaxField>(i, j, k, l, m, n);
								SubDatabase[i][j][k][l][m][n][1].database = NULL;
								SubDatabase[i][j][k][l][m][n][1].db_dtm = NULL;

							}
						}
					}
				}
			}
		}
	}


	// Allocates the new subdabase slice
	///////////////////////////////////////////////////////////////////////////////////////
	template<int BitsPerRow, int MaxField>
	void AllocateNewSubDatabase(int wm, int wk, int bm, int bk, int wmrank, int bmrank)
	{
		//-----------------------------------------------------------------------------------------------------
		// allocate memory for the current dbslice and set the current database to this
		// database.
		//-----------------------------------------------------------------------------------------------------
		bool symmetric = IsSymmetric(wm, wk, bm, bk, wmrank, bmrank);
		//int64 dbsize = SubDatabase[wm][wk][bm][bk][wmrank][bmrank][0].databasesize;
		int64 dbsize = GetDatabaseSize<BitsPerRow, MaxField>(wm, wk, bm, bk, wmrank, bmrank);
		int64 memsize = Pad16(dbsize /4);
		memInUse += memsize + dbsize;
		if(!symmetric)
			memInUse +=memsize + dbsize;
	
		currentDb_w = (uint32*)(new uint8[(size_t)memsize]);
		SubDatabase[wm][wk][bm][bk][wmrank][bmrank][WHITE].database = currentDb_w;

		dtm_W = new uint8[(size_t)dbsize];
		SubDatabase[wm][wk][bm][bk][wmrank][bmrank][WHITE].db_dtm = dtm_W;

		dtm_B = NULL;
		if(!symmetric)
		{
			currentDb_b = (uint32*)(new uint8[(size_t)memsize]);
			dtm_B = new uint8[(size_t)dbsize];
		}
		else
			currentDb_b = NULL;

		SubDatabase[wm][wk][bm][bk][wmrank][bmrank][BLACK].database = currentDb_b;
		SubDatabase[wm][wk][bm][bk][wmrank][bmrank][BLACK].db_dtm = dtm_B;

		if(memInUse > memoryLimit)
		{
				MemoryPanic();
		}
	}


	// Loads the saved state of the currently generated database slice
	///////////////////////////////////////////////////////////////////////////////////////
	template<int BitsPerRow, int MaxField>
	void LoadDBState(int wm, int wk, int bm, int bk, int wmrank, int bmrank, 
						uint32 **cur_w, uint32** cur_b, uint8** dtm_w, uint8** dtm_b, int state)
	{
		wchar_t dbname[PATH_MAX];
		FILE *fp;
		int symmetric = IsSymmetric(wm, wk, bm, bk, wmrank, bmrank);
		int64 memsize = GetDatabaseSize<BitsPerRow, MaxField>(wm, wk, bm, bk, wmrank, bmrank);
		int64 dbsize = Pad16(memsize/4);

		AllocateNewSubDatabase<BitsPerRow, MaxField>(wm, wk, bm, bk, wmrank, bmrank);
		*cur_w = currentDb_w;
		*cur_b = currentDb_b;
		*dtm_w = dtm_W;
		*dtm_b = dtm_B;

		SetDbNameState(wm, wk, bm, bk, wmrank, bmrank, WHITE, state, dbname);
		_wfopen_s(&fp, dbname,L"rb");
		if(fp!=NULL)
		{
			fread(*cur_w, 1, (size_t)dbsize, fp);
			fclose(fp);
		}
	
		if(!symmetric)
		{
			SetDbNameState(wm, wk, bm, bk, wmrank, bmrank, BLACK, state, dbname);
			_wfopen_s(&fp, dbname,L"rb");
			if(fp!=NULL)
			{
				fread(*cur_b, 1, (size_t)dbsize, fp);
				fclose(fp);
			}	
		}

		SetDbDTMNameState(wm, wk, bm, bk, wmrank, bmrank, WHITE, dbname, state);
		_wfopen_s(&fp, dbname,L"rb");
		if(fp!=NULL)
		{
			fread(*dtm_w, 1, (size_t)memsize, fp);
			fclose(fp);
		}
	
		if(!symmetric)
		{
			SetDbDTMNameState(wm, wk, bm, bk, wmrank, bmrank, BLACK, dbname, state);
			_wfopen_s(&fp, dbname,L"rb");
			if(fp!=NULL)
			{
				fread(*dtm_b, 1, (size_t)memsize, fp);
				fclose(fp);
			}	
		}
	}


	// Saves the current state of generated database slices
	///////////////////////////////////////////////////////////////////////////////////////
	template<int BitsPerRow, int MaxField>
	void SaveDBState(int wm, int wk, int bm, int bk, int wmrank, int bmrank, 
					uint32 *cur_w, uint32* cur_b, uint8* dtm_w, uint8* dtm_b, int state)
	{
		wchar_t dbname[PATH_MAX];

		WATCH_START(T_FILE_WRITE);

		// save databases
		int64 memsize = GetDatabaseSize<BitsPerRow, MaxField>(wm, wk, bm, bk, wmrank, bmrank);
		memsize = Pad16(memsize / 4);
	
		// save white
		FILE* fp;
		SetDbNameState(wm, wk, bm, bk, wmrank, bmrank, WHITE, state, dbname);
		int error;
		if(_wfopen_s(&fp, dbname, L"wb") != 0)
		{
			_get_errno(&error);
		}

		fwrite(cur_w, 1, (size_t)memsize, fp);
		fclose(fp);

		// save black
		if(cur_b)
		{
			SetDbNameState(wm, wk, bm, bk, wmrank, bmrank, BLACK, state, dbname);
			int error;
			if(_wfopen_s(&fp, dbname, L"wb") != 0)
			{
				_get_errno(&error);
			}

			fwrite(cur_b, 1, (size_t)memsize, fp);
			fclose(fp);
		}

		// save DTM white
		memsize = GetDatabaseSize<BitsPerRow, MaxField>(wm, wk, bm, bk, wmrank, bmrank);

		SetDbDTMNameState(wm, wk, bm, bk, wmrank, bmrank, WHITE, dbname, state);
		if(_wfopen_s(&fp, dbname, L"wb") != 0)
		{
			_get_errno(&error);
		}

		fwrite(dtm_w, 1, (size_t)memsize, fp);
		fclose(fp);

		if(dtm_b)
		{
			SetDbDTMNameState(wm, wk, bm, bk, wmrank, bmrank, BLACK, dbname, state);
			if(_wfopen_s(&fp, dbname, L"wb") != 0)
			{
				_get_errno(&error);
			}

			fwrite(dtm_b, 1, (size_t)memsize, fp);
			fclose(fp);
		}

		WATCH_END(T_FILE_WRITE);
	}

}
